#include "ecv_vo.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <unistd.h>

#include <SDL.h>
/*
 * Gstreamer includes
 */


G_DEFINE_TYPE (EcvVo, ecv_vo, G_TYPE_OBJECT);

#define ECV_VO_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ECV_TYPE_VO, EcvVoPrivate))

enum
{
  PROP_0,

  PROP_ECV_WIDTH,
  PROP_ECV_HEIGHT,
  PROP_ECV_BPP,
  PROP_ECV_WCAP
};

struct _EcvVoPrivate
{
  int width;
  int height;
  int bpp;
  char * data;
  gchar * wcap;
  SDL_Surface *screen;
  SDL_Surface *nextframe;
  SDL_Event event;
  SDL_Rect src;
  SDL_Rect dst;
  int quit;
};

static void
ecv_vo_set_property (GObject      *object,
                        guint         property_id,
                        const GValue *value,
                        GParamSpec   *pspec)
{
  EcvVo *self = ECV_VO (object);

  switch (property_id)
    {
    case PROP_ECV_WIDTH:
      self->priv->width = g_value_get_uint (value);
      break;

    case PROP_ECV_HEIGHT:
      self->priv->height = g_value_get_uint (value);
      break;

    case PROP_ECV_BPP:
      self->priv->bpp = g_value_get_uint (value);
      break;

    case PROP_ECV_WCAP:
      self->priv->wcap = g_strdup(g_value_get_string (value));
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
ecv_vo_get_property (GObject    *object,
                        guint       property_id,
                        GValue     *value,
                        GParamSpec *pspec)
{
  EcvVo *self = ECV_VO (object);

  switch (property_id)
    {
    case PROP_ECV_WIDTH:
      g_value_set_uint (value, self->priv->width);
      break;

    case PROP_ECV_HEIGHT:
      g_value_set_uint (value, self->priv->height);
      break;

    case PROP_ECV_BPP:
      g_value_set_uint (value, self->priv->bpp);
      break;

    case PROP_ECV_WCAP:
      g_value_set_string (value, self->priv->wcap);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
ecv_vo_dispose (GObject *gobject)
{
  EcvVo *self = ECV_VO (gobject);

  /* 
   * In dispose, you are supposed to free all types referenced from this
   * object which might themselves hold a reference to self. Generally,
   * the most simple solution is to unref all members on which you own a 
   * reference.
   */

  /* dispose might be called multiple times, so we must guard against
   * calling g_object_unref() on an invalid GObject.
   */

  /*  if (self->priv->an_object)
    {
      g_object_unref (self->priv->an_object);
      }*/

  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_vo_parent_class)->dispose (gobject);
}

static void
ecv_vo_finalize (GObject *gobject)
{
  EcvVo *self = ECV_VO (gobject);

  //  g_free (self->a_string);

  /* Chain up to the parent class */
  G_OBJECT_CLASS (ecv_vo_parent_class)->finalize (gobject);
}

static GObject *
ecv_vo_constructor (GType                  gtype,
                       guint                  n_properties,
                       GObjectConstructParam *properties)
{
  GObject *obj;

  {
    /* Always chain up to the parent constructor */
    EcvVoClass *klass;
    GObjectClass *parent_class;  
    parent_class = G_OBJECT_CLASS (ecv_vo_parent_class);
    obj = parent_class->constructor (gtype, n_properties, properties);
  }
  
  /* update the object state depending on constructor properties */

  return obj;
}

static void
ecv_vo_class_init (EcvVoClass *klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  GParamSpec *pspec;

  gobject_class->dispose = ecv_vo_dispose;
  gobject_class->finalize = ecv_vo_finalize;
  gobject_class->set_property = ecv_vo_set_property;
  gobject_class->get_property = ecv_vo_get_property;

  pspec = g_param_spec_uint ("width",
			     "",
			     "image width",
			     1,
			     65534,
			     320,
			     G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
  
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_WIDTH,
                                   pspec);

  pspec = g_param_spec_uint ("height",
			     "",
			     "image height",
			     1,
			     65534,
			     240,
			     G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_HEIGHT,
                                   pspec);

  pspec = g_param_spec_uint ("bpp",
			     "",
			     "image bytes per pixel",
			     1,
			     65534,
			     24,
			     G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_BPP,
                                   pspec);

  pspec = g_param_spec_string ("wcap",
			       "",
			       "window caption",
			       "ecv",
			       G_PARAM_READWRITE);
  g_object_class_install_property (gobject_class,
                                   PROP_ECV_WCAP,
                                   pspec);

  g_type_class_add_private (klass, sizeof (EcvVoPrivate));
  gobject_class->constructor = ecv_vo_constructor;
}

static void
ecv_vo_init (EcvVo *self)
{
  self->priv = ECV_VO_GET_PRIVATE (self);
  self->priv->quit = 0;
  //  self->priv->an_object = g_object_new (ECV_TYPE_BA, NULL);
}

//------------------------------------------------------
/*                     methods                        */
void
ecv_vo_create (EcvVo *self)
{
  g_return_if_fail (ECV_IS_VO (self));
  Uint32 flags = SDL_DOUBLEBUF;
  if (SDL_Init(SDL_INIT_VIDEO) != 0) 
    {
      printf("unable to initialize SDL: %s\n", SDL_GetError());
      return -2;
    }

  atexit(SDL_Quit);
 
  self->priv->screen = SDL_SetVideoMode(self->priv->width, self->priv->height, self->priv->bpp, flags);
  if (!self->priv->screen) 
    {
      printf("Unable to set video mode: %s\n", SDL_GetError());
      return -1;
    }
  self->priv->nextframe = SDL_CreateRGBSurface(self->priv->screen->flags, self->priv->screen->w, self->priv->screen->h, self->priv->bpp,0,0,0,0);
  self->priv->src.x = 0;  self->priv->src.y = 0;  self->priv->src.w = self->priv->width;  self->priv->src.h = self->priv->height;
  self->priv->dst.x = 0;  self->priv->dst.y = 0;  self->priv->dst.w = self->priv->width;  self->priv->dst.h = self->priv->height;
  SDL_WM_SetCaption( self->priv->wcap, NULL );
}

void
ecv_vo_set(EcvVo *self, char * data, int size)
{
  g_return_if_fail (ECV_IS_VO (self));
  SDL_LockSurface(self->priv->nextframe);
  memcpy(self->priv->nextframe->pixels, data, size);
  SDL_UnlockSurface(self->priv->nextframe);
}

void
ecv_vo_draw (EcvVo *self)
{
  g_return_if_fail (ECV_IS_VO (self));
  SDL_BlitSurface(self->priv->nextframe, &(self->priv->src), self->priv->screen, &(self->priv->dst));
  SDL_Flip(self->priv->screen);
}

int
ecv_vo_get_quit(EcvVo *self)
{
  g_return_if_fail (ECV_IS_VO (self));
  return self->priv->quit;
}

void
ecv_vo_events (EcvVo *self)
{
  while( SDL_PollEvent( &self->priv->event ) ) 
    { 
      if( self->priv->event.type == SDL_QUIT ) 
	{
	  self->priv->quit = 1;
	} 
    }
}

//------------------------------------------------------

EcvVo * 
ecv_vo_new (int width, int height, int bpp, char * wcap)
{
  return g_object_new (ECV_TYPE_VO,
		       "width", width,
		       "height", height,
		       "bpp", bpp,
		       "wcap", wcap,
		       NULL);
}
